﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;

namespace SL3.ControlLibrary
{
    
    [Description("A carousel panel for displaying elements")]
    public class CarouselPanel : BaseContainer 
    {
        public CarouselPanel( ) 
            : base () 
        {
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            }

            this.Background = new SolidColorBrush(Colors.Blue);

            this.MouseMove += new MouseEventHandler(CarouselPanel_MouseMove);
            this.MouseEnter += new MouseEventHandler(CarouselPanel_MouseEnter);
            this.MouseLeave += new MouseEventHandler(CarouselPanel_MouseLeave);
        }

        protected DispatcherTimer _timer;

        #region Internal EventHandler

        void CarouselPanel_MouseLeave(object sender, MouseEventArgs e)
        {
            if (_timer != null)
            {
                _timer.Stop();
                _timer.Tick -= _timer_Tick;
            }
        }

        void CarouselPanel_MouseEnter(object sender, MouseEventArgs e)
        {
            if (_timer == null)
            {
                _timer = new DispatcherTimer();
                _timer.Tick += new EventHandler(_timer_Tick);
            }

            _timer.Interval = this.FrameRate;
            _timer.Start();  
        }
        
        void _timer_Tick(object sender, EventArgs e)
        {
            DoArrange();
        }

        void CarouselPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (UseMousePosition)
            {
                Point p = e.GetPosition(this);
                double diffX = (this.ActualWidth / 2) - p.X;
                if (Math.Abs(diffX) > (this.ItemWidth / 2))
                {
                    _internalSpeed = -1 * Speed * (diffX / 100);
                }
                else
                {
                    _internalSpeed = 0d;
                }
            }
        }

        //void CompositionTarget_Rendering(object sender, EventArgs e)
        //{
        //    //DoArrange();
        //}

        #endregion

        #region Internal overrides

        protected override Size ArrangeOverride(System.Windows.Size finalSize)
        {
            for (int i = 0; i < Children.Count; i++)
            {
                // Calculate and set angle of all children
                double angle = i * (Math.PI * 2) / Children.Count;
                Children[i].SetValue(CarouselPanel.AngleProperty, angle);
            }
            DoArrange();

            return base.ArrangeOverride(finalSize);
        }
        
        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement element in Children)
            {
                element.Measure(new Size(ItemWidth, ItemHeight));
            }
            return availableSize;
        }

        #endregion

        #region Properties

        #region ItemWidth

        /// <summary>
        /// Hallo Welt
        /// </summary>
        [Category("Olivers Settings")]
        [Description("la la la")]
        public double ItemWidth
        {
            get { return (double)GetValue(ItemWidthProperty); }
            set { SetValue(ItemWidthProperty, value); }
        }

        public static readonly DependencyProperty ItemWidthProperty =
            DependencyProperty.Register("ItemWidth", typeof(double), typeof(BaseContainer),
            new PropertyMetadata(100.0, OnItemWidthChanged));

        private static void OnItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BaseContainer baseContainer = d as BaseContainer;
            baseContainer.InvalidateArrange();
        }

        #endregion

        #region ItemHeight

        public double ItemHeight
        {
            get { return (double)GetValue(ItemHeightProperty); }
            set { SetValue(ItemHeightProperty, value); }
        }

        public static readonly DependencyProperty ItemHeightProperty =
            DependencyProperty.Register("ItemHeight", typeof(double), typeof(BaseContainer),
            new PropertyMetadata(100.0, OnItemHeightChanged));

        private static void OnItemHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BaseContainer baseContainer = d as BaseContainer;
            baseContainer.InvalidateArrange();
        }

        #endregion


        #region Internal Speed

        private double _internalSpeed = 1;

        public double Speed
        {
            get { return (double)GetValue(SpeedProperty); }
            set 
            { 
                SetValue(SpeedProperty, value);
                this.InvalidateArrange(); 
            }
        }

        public static readonly DependencyProperty SpeedProperty =
                DependencyProperty.Register("Speed", typeof(double), 
                typeof(CarouselPanel), 
                new PropertyMetadata (1.0, 
                new PropertyChangedCallback(OnSpeedChanged) ));

        protected static void OnSpeedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            CarouselPanel panel = obj as CarouselPanel;
            panel._internalSpeed = (double)args.NewValue;
        }

        #endregion

        #region Use Mouse Position

        public bool UseMousePosition
        {
            get { return (bool)GetValue(UseMousePositionProperty); }
            set { SetValue(UseMousePositionProperty, value); this.InvalidateArrange(); } 
        } 
   
        public static readonly DependencyProperty UseMousePositionProperty =
                DependencyProperty.Register("UseMousePosition"
                , typeof(bool), typeof(CarouselPanel), new PropertyMetadata(true));

        #endregion

        #region Scale Perspective

        public double ScalePerspective
        {
            get
            {
                double val = (double)GetValue(ScalePerspectiveProperty);
                return val;
            }
            set { SetValue(ScalePerspectiveProperty, value); this.InvalidateArrange(); }
        }

        public static readonly DependencyProperty ScalePerspectiveProperty =
                DependencyProperty.Register("ScalePerspective", typeof(double), 
                typeof(CarouselPanel), 
                new PropertyMetadata ( 0.3,
                new PropertyChangedCallback(OnScalePerspectiveChanged)));

        protected static void OnScalePerspectiveChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
        {
            CarouselPanel panel = obj as CarouselPanel; 
            if (panel != null) 
            { 
                panel.InvalidateArrange(); 
            }
        }

        #endregion 

        #region Angle

        public static readonly DependencyProperty AngleProperty =
           DependencyProperty.RegisterAttached("Angle", typeof(double), typeof(CarouselPanel), null);
        
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set
            {
                SetValue(AngleProperty, value);
                this.InvalidateArrange(); 
            }
        }

        #endregion

        #endregion

        void DoArrange()
        {
            double height = this.DesiredSize.Height;
            double width = this.DesiredSize.Width;

            Point center = new Point((width - ItemWidth) / 2, (height - ItemHeight) / 2);
            double radiusX = center.X;
            double radiusY = center.Y; 
            double scale = ScalePerspective; 

            for (int i = 0; i < Children.Count; i++)
            {
                UIElement item = Children[i];

                double current = (double)item.GetValue(CarouselPanel.AngleProperty);
                double angle = current + (_internalSpeed * (3 / 360.0) * (2 * Math.PI));
                item.SetValue(CarouselPanel.AngleProperty, angle);
                double radians = angle;

                Point p = new Point(
                    (Math.Cos(radians) * radiusX) + center.X,
                    (Math.Sin(radians) * radiusY) + center.Y);
            
                if (item.RenderTransform == null || 
                    (!(item.RenderTransform is ScaleTransform)))
                {
                    item.RenderTransform = new ScaleTransform();
                }
                
                ScaleTransform sc = item.RenderTransform as ScaleTransform;

                double scaleMinusRounding  = p.Y / (center.Y + radiusY); 
                sc.ScaleX = sc.ScaleY =  Math.Min(scaleMinusRounding + scale, 1.0) ; 

                int zIndex = (int) ((p.Y / this.Height) * 50);                   
                item.SetValue(Canvas.ZIndexProperty, zIndex);

                Rect r = new Rect(p.X, p.Y, ItemWidth, ItemHeight);
                item.Arrange(r);
                item.InvalidateArrange();
             }
        }
    }
}
